// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/viz/service/main/viz_main_impl.h"

#include <memory>
#include <utility>

#include "base/memory/raw_ptr.h"
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_source.h"
#include "base/test/task_environment.h"
#include "gpu/config/gpu_info.h"
#include "gpu/ipc/service/gpu_init.h"
#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_entry_builder.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace viz {

// This mock lets us listen for delegate messages generated by a |VizMainImpl|.
// We use this to check that Viz's task runner is the task runner specified by
// dependency injection.
class MockDelegate : public VizMainImpl::Delegate {
 public:
  MOCK_METHOD1(PostCompositorThreadCreated,
               void(base::SingleThreadTaskRunner*));
  MOCK_METHOD0(OnInitializationFailed, void());
  MOCK_METHOD1(OnGpuServiceConnection, void(GpuServiceImpl*));
  MOCK_METHOD0(QuitMainMessageLoop, void());
};

// This mock lets us listen for UKM events to be added. We use this to verify
// that the dependency-injected UKM recorder actually gets used.
class MockUkmRecorder : public ukm::MojoUkmRecorder {
 public:
  MockUkmRecorder()
      : ukm::MojoUkmRecorder(
            mojo::PendingRemote<ukm::mojom::UkmRecorderInterface>()) {}

  MOCK_METHOD1(AddEntry, void(ukm::mojom::UkmEntryPtr));
};

class MockVizCompositorThreadRunner : public VizCompositorThreadRunner {
 public:
  explicit MockVizCompositorThreadRunner(
      base::SingleThreadTaskRunner* task_runner)
      : VizCompositorThreadRunner(), task_runner_(task_runner) {}

  base::PlatformThreadId thread_id() override {
    return base::PlatformThreadId();
  }
  base::SingleThreadTaskRunner* task_runner() override { return task_runner_; }
  MOCK_METHOD1(CreateFrameSinkManager, void(mojom::FrameSinkManagerParamsPtr));
  MOCK_METHOD4(CreateFrameSinkManager,
               void(mojom::FrameSinkManagerParamsPtr,
                    gpu::CommandBufferTaskExecutor*,
                    GpuServiceImpl*,
                    HintSessionFactory*));

 private:
  const raw_ptr<base::SingleThreadTaskRunner> task_runner_;
};

class MockPowerMonitorSource : public base::PowerMonitorSource {
 public:
  explicit MockPowerMonitorSource(bool* leak_guard)
      : base::PowerMonitorSource(), leak_guard_(leak_guard) {
    *leak_guard_ = true;
  }

  ~MockPowerMonitorSource() override { *leak_guard_ = false; }

  bool IsOnBatteryPower() override { return false; }

 private:
  // An external flag to signal as to whether or not this object is still
  // alive.
  raw_ptr<bool> leak_guard_;
};

TEST(VizMainImplTest, OopVizDependencyInjection) {
  VizMainImpl::ExternalDependencies external_deps;
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      base::ThreadTaskRunnerHandle::Get();

  // |VizMainImpl| is supposed to use the |UkmRecorder| injected through
  // |ExternalDependencies|.
  std::unique_ptr<MockUkmRecorder> mock_ukm_recorder =
      std::make_unique<MockUkmRecorder>();
  EXPECT_CALL(*mock_ukm_recorder, AddEntry);
  external_deps.ukm_recorder = std::move(mock_ukm_recorder);

  // |VizMainImpl| is supposed to use the task runner injected through
  // |ExternalDependencies|. We can check which task runner |VizMainImpl| will
  // use by looking for the task runner reported to the delegate.
  MockDelegate mock_delegate;
  EXPECT_CALL(mock_delegate, PostCompositorThreadCreated(task_runner.get()));
  MockVizCompositorThreadRunner mock_runner(task_runner.get());
  external_deps.viz_compositor_thread_runner = &mock_runner;

  bool mock_source_is_alive = false;
  external_deps.power_monitor_source =
      std::make_unique<MockPowerMonitorSource>(&mock_source_is_alive);
  ASSERT_TRUE(mock_source_is_alive);

  auto gpu_init = std::make_unique<gpu::GpuInit>();
  // Need to force GpuInit to request an OOP viz; if |GpuInit| stops owning the
  // |GPUInfo|, this const_cast may break.
  const_cast<gpu::GPUInfo&>(gpu_init->gpu_info()).in_process_gpu = false;

  VizMainImpl impl(&mock_delegate, std::move(external_deps),
                   std::move(gpu_init));

  // Generate a UKM event so that the MockRecorder sees it. We use the global
  // recorder from |ukm::UkmRecorder::Get()| because that's the recorder that
  // client code is expected to use. That the correct recorder sees the entry
  // is handled with the EXPECT_CALL of |mock_ukm_recorder|.
  ukm::UkmRecorder* recorder = ukm::UkmRecorder::Get();
  ukm::UkmEntryBuilder builder(ukm::SourceId(42), "Event.ScrollUpdate.Touch");
  builder.SetMetric("TimeToScrollUpdateSwapBegin", 17);
  builder.Record(recorder);

  // Need to shutdown the |PowerMonitor| infrastructure.
  EXPECT_TRUE(base::PowerMonitor::IsInitialized());
  base::PowerMonitor::ShutdownForTesting();
  // Double-check that we're not leaking the MockPowerMonitorSource
  // instance.
  ASSERT_FALSE(mock_source_is_alive);
}

}  // namespace viz
